package com.matrix.gradle

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.GradleException
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.JavaExec

/**
 * Matrix Build Extension Plugin
 */
class MatrixBuildPlugin implements Plugin<Project> {
    
    @Override
    void apply(Project project) {
        // Create branch management tasks
        createBranchTasks(project)
        
        // Create version management tasks
        createVersionTasks(project)
        
        // Create release management tasks
        createReleaseTasks(project)
        
        // Create build management tasks
        createBuildTasks(project)
        
        // Create database management tasks
        createDatabaseTasks(project)
        
        // Create frontend integration tasks
        createFrontendTasks(project)
    }
    
    /**
     * Create branch management tasks
     */
    private void createBranchTasks(Project project) {
        project.task('createBranch') {
            group = 'matrix/branch'
            description = 'Create a new Git branch'
            
            doLast {
                def branchName = project.findProperty('branchName')
                def baseBranch = project.findProperty('baseBranch')
                
                if (!branchName) {
                    throw new GradleException("Please specify branch name: -PbranchName=<branch-name>")
                }
                
                if (!VersionUtils.isValidBranchName(branchName)) {
                    throw new GradleException("Invalid branch name: ${branchName}")
                }
                
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                VersionUtils.createBranch(branchName, baseBranch, project.projectDir)
                println "Branch creation completed!"
                if (baseBranch) {
                    println "Base branch: ${baseBranch}"
                }
                println "New branch: ${branchName}"
            }
        }
    }
    
    /**
     * Create version management tasks
     */
    private void createVersionTasks(Project project) {
        // 主版本递增
        project.task('tagMajor') {
            group = 'matrix/version'
            description = '递增主版本号并创建标签 (x.y.z -> (x+1).0.0)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def currentVersion = VersionUtils.getLatestVersion(project.projectDir)
                def newVersion = VersionUtils.incrementMajor(currentVersion)
                
                VersionUtils.createTag(newVersion, "Major release ${newVersion}", project.projectDir)
                
                println "Version upgrade completed!"
                println "Current version: ${currentVersion} -> New version: ${newVersion}"
            }
        }
        
        // 次版本递增
        project.task('tagMinor') {
            group = 'matrix/version'
            description = '递增次版本号并创建标签 (x.y.z -> x.(y+1).0)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def currentVersion = VersionUtils.getLatestVersion(project.projectDir)
                def newVersion = VersionUtils.incrementMinor(currentVersion)
                
                VersionUtils.createTag(newVersion, "Minor release ${newVersion}", project.projectDir)
                
                println "Version upgrade completed!"
                println "Current version: ${currentVersion} -> New version: ${newVersion}"
            }
        }
        
        // 修订版本递增
        project.task('tagPatch') {
            group = 'matrix/version'
            description = '递增修订版本号并创建标签 (x.y.z -> x.y.(z+1))'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def currentVersion = VersionUtils.getLatestVersion(project.projectDir)
                def newVersion = VersionUtils.incrementPatch(currentVersion)
                
                VersionUtils.createTag(newVersion, "Patch release ${newVersion}", project.projectDir)
                
                println "Version upgrade completed!"
                println "Current version: ${currentVersion} -> New version: ${newVersion}"
            }
        }
    }
    
    /**
     * Create release management tasks
     */
    private void createReleaseTasks(Project project) {
        // Alpha 版本
        project.task('tagAlpha') {
            group = 'matrix/release'
            description = '创建 Alpha 预发布版本标签 (x.y.z-alpha.n)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def baseVersion = VersionUtils.getLatestVersion(project.projectDir)
                def alphaNumber = VersionUtils.getNextPreReleaseNumber(baseVersion, 'alpha', project.projectDir)
                def newVersion = "${baseVersion}-alpha.${alphaNumber}"
                
                VersionUtils.createTag(newVersion, "Alpha release ${newVersion}", project.projectDir)
                
                println "Alpha version creation completed!"
                println "Base version: ${baseVersion} -> Alpha version: ${newVersion}"
            }
        }
        
        // Beta 版本
        project.task('tagBeta') {
            group = 'matrix/release'
            description = '创建 Beta 预发布版本标签 (x.y.z-beta.n)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def baseVersion = VersionUtils.getLatestVersion(project.projectDir)
                def betaNumber = VersionUtils.getNextPreReleaseNumber(baseVersion, 'beta', project.projectDir)
                def newVersion = "${baseVersion}-beta.${betaNumber}"
                
                VersionUtils.createTag(newVersion, "Beta release ${newVersion}", project.projectDir)
                
                println "Beta version creation completed!"
                println "Base version: ${baseVersion} -> Beta version: ${newVersion}"
            }
        }
        
        // Release 版本
        project.task('tagRelease') {
            group = 'matrix/release'
            description = '创建 Release 稳定版本标签 (x.y.z-release)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def baseVersion = VersionUtils.getLatestVersion(project.projectDir)
                def newVersion = "${baseVersion}-release"
                
                VersionUtils.createTag(newVersion, "Stable release ${newVersion}", project.projectDir)
                
                println "Release version creation completed!"
                println "Base version: ${baseVersion} -> Release version: ${newVersion}"
            }
        }
        
        // LTS 版本
        project.task('tagLts') {
            group = 'matrix/release'
            description = '创建 LTS 长期支持版本标签 (x.y.z-lts)'
            
            doLast {
                if (!VersionUtils.isGitRepository(project.projectDir)) {
                    throw new GradleException("Not a Git repository")
                }
                
                def baseVersion = VersionUtils.getLatestVersion(project.projectDir)
                def newVersion = "${baseVersion}-lts"
                
                VersionUtils.createTag(newVersion, "Long Term Support release ${newVersion}", project.projectDir)
                
                println "LTS version creation completed!"
                println "Base version: ${baseVersion} -> LTS version: ${newVersion}"
            }
        }
    }
    
    /**
     * Create build management tasks
     */
    private void createBuildTasks(Project project) {
        // Build WAR package with frontend integration
        project.task('buildWar') {
            group = 'matrix'
            description = 'Build WAR package for web deployment (includes frontend integration)'
            
            doFirst {
                // Check if war plugin is applied
                if (!project.plugins.hasPlugin('war')) {
                    throw new GradleException("WAR plugin is not applied. Please add 'id \"war\"' to your plugins block in build.gradle")
                }
                
                // Auto integrate frontend if frontendPath is provided
                def frontendPath = project.findProperty('frontendPath')
                if (frontendPath) {
                    println "🔄 Auto-integrating frontend before WAR build..."
                    integrateFrontendResources(project, frontendPath)
                } else {
                    println "ℹ️  No frontendPath specified, building WAR without frontend integration"
                    println "ℹ️  To include frontend: ./gradlew buildWar -PfrontendPath=../matrix-vben"
                }
            }
            
            dependsOn 'war'
            
            doLast {
                def warFile = project.tasks.war.archiveFile.get().asFile
                if (warFile.exists()) {
                    println ""
                    println "🎉 WAR Build Completed Successfully!"
                    println "✓ Package name: ${warFile.name}"
                    println "✓ File location: ${warFile.absolutePath}"
                    println "✓ Package size: ${String.format('%.2f', warFile.length() / 1024.0 / 1024.0)} MB"
                    
                    def frontendPath = project.findProperty('frontendPath')
                    if (frontendPath) {
                        println "✓ Frontend integrated: Yes"
                        println "✓ Frontend source: ${frontendPath}"
                    } else {
                        println "✓ Frontend integrated: No"
                    }
                    
                    println ""
                    println "📋 Deployment Instructions:"
                    println "1. Copy ${warFile.name} to your web server"
                    println "2. Deploy to Tomcat webapps directory"
                    println "3. Start/restart Tomcat server"
                } else {
                    throw new GradleException("WAR file not found after build")
                }
            }
        }
        
        // Build executable JAR package with frontend integration
        project.task('buildExecutableJar') {
            group = 'matrix'
            description = 'Build executable JAR package with all dependencies (includes frontend integration)'
            
            doFirst {
                // Auto integrate frontend if frontendPath is provided
                def frontendPath = project.findProperty('frontendPath')
                if (frontendPath) {
                    println "🔄 Auto-integrating frontend before JAR build..."
                    integrateFrontendResources(project, frontendPath)
                } else {
                    println "ℹ️  No frontendPath specified, building JAR without frontend integration"
                    println "ℹ️  To include frontend: ./gradlew buildExecutableJar -PfrontendPath=../matrix-vben"
                }
            }
            
            dependsOn 'bootJar'
            
            doLast {
                def jarFile = project.tasks.bootJar.archiveFile.get().asFile
                if (jarFile.exists()) {
                    println ""
                    println "🎉 Executable JAR Build Completed Successfully!"
                    println "✓ Package name: ${jarFile.name}"
                    println "✓ File location: ${jarFile.absolutePath}"
                    println "✓ Package size: ${String.format('%.2f', jarFile.length() / 1024.0 / 1024.0)} MB"
                    
                    def frontendPath = project.findProperty('frontendPath')
                    if (frontendPath) {
                        println "✓ Frontend integrated: Yes"
                        println "✓ Frontend source: ${frontendPath}"
                    } else {
                        println "✓ Frontend integrated: No"
                    }
                    
                    println ""
                    println "📋 Run Instructions:"
                    println "java -jar ${jarFile.name}"
                    println ""
                    println "📋 Optional JVM Arguments:"
                    println "java -Xmx2g -Dspring.profiles.active=prod -jar ${jarFile.name}"
                } else {
                    throw new GradleException("Executable JAR file not found after build")
                }
            }
        }
        
        // Build both WAR and JAR with frontend integration
        project.task('buildAll') {
            group = 'matrix'
            description = 'Build both WAR and executable JAR packages (includes frontend integration)'
            dependsOn 'buildWar', 'buildExecutableJar'
            
            doLast {
                println ""
                println "🎉 All Build Tasks Completed Successfully!"
                println "✓ WAR package: Ready for web server deployment"
                println "✓ JAR package: Ready for standalone execution"
                
                def frontendPath = project.findProperty('frontendPath')
                if (frontendPath) {
                    println "✓ Frontend integration: Completed"
                } else {
                    println "ℹ️  Frontend integration: Skipped (no frontendPath specified)"
                }
            }
        }
    }
    
    /**
     * Create database management tasks
     */
    private void createDatabaseTasks(Project project) {
        // Add mysql configuration if not exists
        if (!project.configurations.findByName('mysql')) {
            project.configurations.create('mysql')
        }
        
        // Add MariaDB dependency to mysql configuration
        project.dependencies.add('mysql', 'org.mariadb.jdbc:mariadb-java-client:3.3.2')
        
        // Create DatabaseInitializer class task
        project.task('createInitializerClass') {
            doLast {
                def sourceFile = project.file("${project.buildDir}/tmp/DatabaseInitializer.java")
                sourceFile.parentFile.mkdirs()
                sourceFile.setText("""
                    import java.sql.*;
                    import java.io.*;
                    import java.nio.charset.StandardCharsets;
                    import java.nio.file.*;
                    import java.util.List;

                    public class DatabaseInitializer {
                        public static void main(String[] args) {
                            try {
                                Class.forName("org.mariadb.jdbc.Driver");
                                
                                String url = "jdbc:mariadb://localhost:2025/?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
                                Connection conn = DriverManager.getConnection(url, "root", "Matrix.Develop.2025");
                                
                                // Set connection charset
                                Statement init = conn.createStatement();
                                init.execute("SET NAMES utf8mb4");
                                init.close();
                                
                                conn.setAutoCommit(false);
                                
                                try {
                                    Statement stmt = conn.createStatement();
                                    
                                    String[] files = {"matrix.sql"};
                                    for (String file : files) {
                                        File sqlFile = new File("ddl/" + file);
                                        if (sqlFile.exists()) {
                                            System.out.println("✓ Executing SQL file: " + file);
                                            List<String> lines = Files.readAllLines(sqlFile.toPath(), StandardCharsets.UTF_8);
                                            StringBuilder sqlContent = new StringBuilder();
                                            for (String line : lines) {
                                                sqlContent.append(line).append("\\n");
                                            }
                                            
                                            String[] statements = sqlContent.toString().split(";");
                                            for (String sql : statements) {
                                                sql = sql.trim();
                                                if (!sql.isEmpty()) {
                                                    try {
                                                        stmt.execute(sql);
                                                    } catch (SQLException e) {
                                                        System.err.println("✗ SQL execution failed: " + sql);
                                                        throw e;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    
                                    conn.commit();
                                    System.out.println("✓ Database initialization completed successfully");
                                    
                                } catch (Exception e) {
                                    conn.rollback();
                                    throw e;
                                } finally {
                                    conn.close();
                                }
                                
                            } catch (Exception e) {
                                System.err.println("✗ Initialization failed: " + e.getMessage());
                                e.printStackTrace();
                                System.exit(1);
                            }
                        }
                    }
                """, 'UTF-8')
            }
        }
        
        // Compile initializer class task
        project.task('compileInitializer', type: JavaCompile) {
            dependsOn 'createInitializerClass'
            source = project.file("${project.buildDir}/tmp")
            destinationDirectory = project.file("${project.buildDir}/classes/initializer")
            classpath = project.configurations.mysql
            sourceCompatibility = '21'
            targetCompatibility = '21'
            options.encoding = 'UTF-8'
            options.compilerArgs << '-Xlint:unchecked'
        }
        
        // Database initialization task
        project.task('initDB', type: JavaExec) {
            group = 'matrix'
            description = 'Initialize database schema and base data'
            dependsOn 'compileInitializer'
            
            classpath = project.files("${project.buildDir}/classes/initializer") + project.configurations.mysql
            mainClass = 'DatabaseInitializer'
            jvmArgs = ['-Dfile.encoding=UTF-8']
        }
    }
    
    /**
     * Create frontend integration tasks
     */
    private void createFrontendTasks(Project project) {
        // Frontend integration task
        project.task('integrateFrontend') {
            group = 'matrix'
            description = 'Integrate frontend static resources into backend project'
            
            doLast {
                def frontendPath = project.findProperty('frontendPath')
                
                if (!frontendPath) {
                    throw new GradleException("Please specify frontend path: -PfrontendPath=<path>")
                }
                
                def frontendDir = resolveFrontendPath(frontendPath, project.projectDir)
                def frontendDistDir = new File(frontendDir, 'apps/web-antd/dist')
                def backendStaticDir = new File(project.projectDir, 'src/main/resources/static')
                
                // Validate frontend directory
                if (!frontendDir.exists()) {
                    throw new GradleException("Frontend directory does not exist: ${frontendDir.absolutePath}")
                }
                
                if (!frontendDistDir.exists()) {
                    throw new GradleException("Frontend dist directory does not exist: ${frontendDistDir.absolutePath}. Please build the frontend project first.")
                }
                
                // Clean previous integration
                println "🧹 Cleaning previous frontend integration..."
                if (backendStaticDir.exists()) {
                    backendStaticDir.deleteDir()
                }
                backendStaticDir.mkdirs()
                
                // Copy frontend dist to backend static
                println "📦 Integrating frontend resources..."
                project.copy {
                    from frontendDistDir
                    into backendStaticDir
                }
                
                // Calculate and display statistics
                def copiedFiles = countFiles(backendStaticDir)
                def totalSize = calculateDirectorySize(backendStaticDir)
                
                println "✅ Frontend integration completed successfully!"
                println "📁 Frontend source: ${frontendDistDir.absolutePath}"
                println "📁 Backend target: ${backendStaticDir.absolutePath}"
                println "📊 Files copied: ${copiedFiles}"
                println "📊 Total size: ${formatFileSize(totalSize)}"
                
                // Display integration summary
                displayIntegrationSummary(backendStaticDir)
            }
        }
    }
    
    /**
     * Resolve frontend path (absolute or relative)
     */
    private File resolveFrontendPath(String frontendPath, File projectDir) {
        def path = new File(frontendPath)
        
        // If absolute path, use directly
        if (path.isAbsolute()) {
            return path
        }
        
        // If relative path, resolve relative to project parent directory
        def parentDir = projectDir.parentFile
        return new File(parentDir, frontendPath)
    }
    
    /**
     * Count files in directory recursively
     */
    private int countFiles(File dir) {
        if (!dir.exists() || !dir.isDirectory()) {
            return 0
        }
        
        int count = 0
        dir.eachFileRecurse { file ->
            if (file.isFile()) {
                count++
            }
        }
        return count
    }
    
    /**
     * Calculate directory size recursively
     */
    private long calculateDirectorySize(File dir) {
        if (!dir.exists() || !dir.isDirectory()) {
            return 0
        }
        
        long size = 0
        dir.eachFileRecurse { file ->
            if (file.isFile()) {
                size += file.length()
            }
        }
        return size
    }
    
    /**
     * Format file size in human readable format
     */
    private String formatFileSize(long bytes) {
        if (bytes < 1024) return "${bytes} B"
        if (bytes < 1024 * 1024) return String.format("%.2f KB", bytes / 1024.0)
        if (bytes < 1024 * 1024 * 1024) return String.format("%.2f MB", bytes / (1024.0 * 1024.0))
        return String.format("%.2f GB", bytes / (1024.0 * 1024.0 * 1024.0))
    }
    
    /**
     * Display integration summary
     */
    private void displayIntegrationSummary(File staticDir) {
        println ""
        println "📋 Integration Summary:"
        println "├── Static Resources Structure:"
        
        def maxDepth = 2
        displayDirectoryTree(staticDir, "│   ", 0, maxDepth)
        
        println "└── Integration completed at ${new Date().format('yyyy-MM-dd HH:mm:ss')}"
    }
    
    /**
     * Display directory tree structure
     */
    private void displayDirectoryTree(File dir, String prefix, int currentDepth, int maxDepth) {
        if (currentDepth >= maxDepth || !dir.exists() || !dir.isDirectory()) {
            return
        }
        
        def files = dir.listFiles()?.sort { it.name }
        if (!files) return
        
        files.eachWithIndex { file, index ->
            def isLast = (index == files.length - 1)
            def connector = isLast ? "└── " : "├── "
            def nextPrefix = isLast ? "    " : "│   "
            
            if (file.isDirectory()) {
                println "${prefix}${connector}📁 ${file.name}/"
                displayDirectoryTree(file, prefix + nextPrefix, currentDepth + 1, maxDepth)
            } else {
                def fileSize = formatFileSize(file.length())
                println "${prefix}${connector}📄 ${file.name} (${fileSize})"
            }
        }
    }
    
    /**
     * Internal method to integrate frontend resources
     * Used by build tasks to auto-integrate frontend
     */
    private void integrateFrontendResources(Project project, String frontendPath) {
        def frontendDir = resolveFrontendPath(frontendPath, project.projectDir)
        def frontendDistDir = new File(frontendDir, 'apps/web-antd/dist')
        def backendStaticDir = new File(project.projectDir, 'src/main/resources/static')
        
        // Validate frontend directory
        if (!frontendDir.exists()) {
            throw new GradleException("Frontend directory does not exist: ${frontendDir.absolutePath}")
        }
        
        if (!frontendDistDir.exists()) {
            throw new GradleException("Frontend dist directory does not exist: ${frontendDistDir.absolutePath}. Please build the frontend project first.")
        }
        
        // Clean previous integration
        println "🧹 Cleaning previous frontend integration..."
        if (backendStaticDir.exists()) {
            backendStaticDir.deleteDir()
        }
        backendStaticDir.mkdirs()
        
        // Copy frontend dist to backend static
        println "📦 Integrating frontend resources..."
        project.copy {
            from frontendDistDir
            into backendStaticDir
        }
        
        // Calculate and display statistics
        def copiedFiles = countFiles(backendStaticDir)
        def totalSize = calculateDirectorySize(backendStaticDir)
        
        println "✅ Frontend integration completed!"
        println "📁 Source: ${frontendDistDir.absolutePath}"
        println "📁 Target: ${backendStaticDir.absolutePath}"
        println "📊 Files: ${copiedFiles}, Size: ${formatFileSize(totalSize)}"
    }
}